package com.zqj.project.common.aspect;

import cn.hutool.core.lang.Assert;
import com.zqj.project.common.annotation.PreventDuplicateSubmit;
import com.zqj.project.common.constant.Constant;
import com.zqj.project.common.exception.BizException;
import jakarta.annotation.Resource;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.logging.log4j.util.Strings;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Description: 防止重复提交 AOP切面
 * @Author: 22001010704 Zhang Qijian
 * @CreateTime: 2024/11/5 17:36
 * @Version: 1.0
 */
@Aspect
@Component
public class PreventDuplicateSubmitAspect {

    @Resource
    private RedisTemplate<String, LocalDateTime> redisTemplate;

    @Around("@annotation(preventDuplicateSubmit)")
    public Object checkDuplicateSubmit(ProceedingJoinPoint proceedingJoinPoint, PreventDuplicateSubmit preventDuplicateSubmit) throws Throwable {
        final String generatedKey = this.generateKey(proceedingJoinPoint, this.getUserId());
        if (Boolean.TRUE.equals(this.redisTemplate.hasKey(generatedKey)) || Boolean.FALSE.equals(this.redisTemplate.opsForValue().setIfAbsent(generatedKey, this.getCurrentDateTime(), preventDuplicateSubmit.expireTime(), preventDuplicateSubmit.timeUnit()))) {
            throw new BizException(preventDuplicateSubmit.message());
        }
        return proceedingJoinPoint.proceed();
    }

    private Long getUserId() {
        try {
            final Long userId = 1L;
            Assert.notNull(userId, Constant.ILLEGAL_ACCESS_MESSAGE);
            return userId;
        } catch (Exception e) {
            throw new IllegalArgumentException(Constant.ILLEGAL_ACCESS_MESSAGE);
        }
    }

    private LocalDateTime getCurrentDateTime() {
        return LocalDateTime.now();
    }

    private String generateKey(ProceedingJoinPoint proceedingJoinPoint, Long userId) {
        final MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        final Object[] args = proceedingJoinPoint.getArgs();
        return String.format("%s:%s:%s:%s:%s", Constant.SUBMITTED, userId, proceedingJoinPoint.getTarget().getClass().getName(), methodSignature.getMethod().getName(), Objects.isNull(args) ? Strings.EMPTY : DigestUtils.sha512Hex(Arrays.stream(args).filter(Objects::nonNull).map(Object::toString).collect(Collectors.joining(Constant.COLON))));
    }

}